import type { ICustomAttributesEntities, JsHandler } from '@/types'
import { createGetCase, fixturesRootPath } from '#test/util'
import { vi } from 'vitest'
import { getCompilerContext } from '@/context'
import { transformUVue } from '@/uni-app-x'
import { replaceWxml } from '@/wxml'

const getCase = createGetCase(fixturesRootPath)

describe('uni-app-x', () => {
  it('index.uvue', async () => {
    const { jsHandler } = getCompilerContext()
    const vueRawCode = await getCase('uni-app-x/index.uvue')
    const classNameSet = new Set<string>()
    classNameSet.add('text-[#258f27]')
    classNameSet.add('text-[100px]')
    classNameSet.add('py-[22.32px]')

    expect(transformUVue(vueRawCode, 'index.uvue', jsHandler, classNameSet)).toMatchSnapshot()
  })

  it('app.uvue', async () => {
    const { jsHandler } = getCompilerContext()
    const vueRawCode = await getCase('uni-app-x/App.uvue')
    const classNameSet = new Set<string>()
    classNameSet.add('text-[#258f27]')
    classNameSet.add('text-[100px]')
    classNameSet.add('py-[22.32px]')

    expect(transformUVue(vueRawCode, 'App.uvue', jsHandler, classNameSet)).toMatchSnapshot()
  })

  it('setup-lang-uts.uvue', async () => {
    const { jsHandler } = getCompilerContext()
    const vueRawCode = await getCase('uni-app-x/setup-lang-uts.uvue')
    const classNameSet = new Set<string>()
    classNameSet.add('text-[#258f27]')
    classNameSet.add('text-[100px]')
    classNameSet.add('py-[22.32px]')
    classNameSet.add('bg-[#000]')
    classNameSet.add('bg-[#111]')
    classNameSet.add('bg-[#222]')

    expect(transformUVue(vueRawCode, 'setup-lang-uts.uvue', jsHandler, classNameSet)).toMatchSnapshot()
  })

  it('transforms static and dynamic class bindings', () => {
    const runtimeSet = new Set<string>()
    const jsHandler: JsHandler = vi.fn((source: string) => ({ code: `handled(${source})` }))
    const code = `
<template>
  <view class="text-[#123]" :class="dynamicCls">
    <text>{{ label }}</text>
  </view>
</template>
<script lang="ts">
const label = 'hi'
</script>
`
    const result = transformUVue(code, 'sample.uvue', jsHandler, runtimeSet)
    expect(result?.code).toContain(`class="${replaceWxml('text-[#123]')}"`)
    expect(result?.code).toContain('handled(dynamicCls)')
    expect(result?.code).toContain('handled(\nconst label = \'hi\'\n)')
    expect(jsHandler).toHaveBeenCalled()
  })

  it('transforms object literal class bindings with whitespace', () => {
    const { jsHandler } = getCompilerContext()
    const runtimeSet = new Set<string>([
      'border-[#ff0000]',
      'bg-blue-600/50',
    ])
    const code = `
<template>
  <view :class="{ 'border-[#ff0000] bg-blue-600/50': isActive }">hello</view>
</template>
`
    const result = transformUVue(code, 'literal.uvue', jsHandler, runtimeSet)
    expect(result?.code).toContain(replaceWxml('border-[#ff0000] bg-blue-600/50'))
  })

  it('transforms array and ternary based vue bindings', () => {
    const { jsHandler } = getCompilerContext()
    const runtimeSet = new Set<string>([
      'bg-[#123456]',
      'bg-[#654321]',
      'text-[#ff0000]',
      'font-bold',
      'border-[#111111]',
      'border-solid',
      'bg-[#999999]',
      'text-[#b01515]',
    ])
    const code = `
<template>
  <view :class="[
      flag ? 'bg-[#123456]' : 'bg-[#654321]',
      extra,
      { 'text-[#ff0000] font-bold': toggled },
      condition && 'border-[#111111] border-solid'
    ]">
    complex
  </view>
  <text class="" :class="condition ? 'bg-[#999999] text-[#b01515]' : ''">fallback</text>
  <text :class="">empty</text>
</template>
<script setup lang="ts">
const flag = true
const extra = 'font-bold'
const toggled = true
const condition = true
</script>
`
    const result = transformUVue(code, 'complex.uvue', jsHandler, runtimeSet)
    expect(result?.code).toContain(`'${replaceWxml('bg-[#123456]')}'`)
    expect(result?.code).toContain(`'${replaceWxml('bg-[#654321]')}'`)
    expect(result?.code).toContain(`'${replaceWxml('text-[#ff0000] font-bold')}'`)
    expect(result?.code).toContain(`'${replaceWxml('border-[#111111] border-solid')}'`)
    expect(result?.code).toContain(replaceWxml('bg-[#999999]'))
    expect(result?.code).toContain(replaceWxml('text-[#b01515]'))
  })

  it('respects customAttributes for static and dynamic bindings', () => {
    const { jsHandler } = getCompilerContext()
    const runtimeSet = new Set<string>([
      'bg-[#121212]',
      'bg-[#343434]',
      'text-[#565656]',
    ])
    const customAttributesEntities: ICustomAttributesEntities = [
      ['*', ['foo-class']],
      ['view', ['bar-class']],
    ]
    const code = `
<template>
  <view foo-class="bg-[#121212]" :foo-class="condition ? 'bg-[#343434]' : ''">
    <view :bar-class="'text-[#565656]'">inner</view>
  </view>
</template>
<script setup lang="ts">
const condition = true
</script>
`
    const result = transformUVue(code, 'custom.uvue', jsHandler, runtimeSet, {
      customAttributesEntities,
    })
    expect(result?.code).toContain(`foo-class="${replaceWxml('bg-[#121212]')}"`)
    expect(result?.code).toContain(replaceWxml('bg-[#343434]'))
    expect(result?.code).toContain(replaceWxml('text-[#565656]'))
  })

  it('honors disabledDefaultTemplateHandler with custom class rules', () => {
    const { jsHandler } = getCompilerContext()
    const runtimeSet = new Set<string>([
      'bg-[#abcdef]',
      'bg-[#fedcba]',
    ])
    const customAttributesEntities: ICustomAttributesEntities = [
      ['*', ['class']],
    ]
    const code = `
<template>
  <view class="bg-[#abcdef]" :class="'bg-[#fedcba]'">content</view>
</template>
`
    const result = transformUVue(code, 'disabled-default.uvue', jsHandler, runtimeSet, {
      customAttributesEntities,
      disabledDefaultTemplateHandler: true,
    })
    expect(result?.code).toContain(replaceWxml('bg-[#abcdef]'))
    expect(result?.code).toContain(replaceWxml('bg-[#fedcba]'))
  })

  it('ignores non-uvue files', () => {
    const jsHandler: JsHandler = vi.fn((source: string) => ({ code: source }))
    const result = transformUVue('<template><view/></template>', 'App.vue', jsHandler, new Set())
    expect(result).toBeUndefined()
    expect(jsHandler).not.toHaveBeenCalled()
  })
})
